package com.zhiwei.codesecurity.web.common.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.zhiwei.codesecurity.common.exception.ParamException;
import com.zhiwei.codesecurity.common.utils.AESSupport;
import com.zhiwei.codesecurity.web.common.base.RequestHead;
import com.zhiwei.codesecurity.web.common.constants.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.server.ServletServerHttpRequest;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.zhiwei.codesecurity.web.common.annotation.XnParamParser;

public class MyHttpMessageConverter extends FastJsonHttpMessageConverter {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    // 参数是否需要解密
    @Value("${data.encryptFlag}")
    boolean encryptFlag;

    // 解密Key密钥
    @Value("${data.encryptKey}")
    String AESKey;

    // 请求URL
    private final ThreadLocal<String> url = new ThreadLocal<>();

    // 不需要加密请求列表
    private List<String> noNeedSecretServices;

    @SuppressWarnings("unchecked")
    @Override
    protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException,
            HttpMessageNotReadableException {
        Object rlt = null;
        String apiUrl = null;
        if (inputMessage instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest request = (ServletServerHttpRequest) inputMessage;
            Map<String, Object> data = (Map<String, Object>)request.getServletRequest().getAttribute(Constants.REQUEST_PARAMS);
            if (null == data) {
                return null;
            }
            Object obj;
            if (RequestHead.class == clazz) {
                obj = data.get(Constants.API_PARAMS_HEADER_KEY);
            } else {
                obj = data.get(Constants.API_PARAMS_BODY_KEY);
            }
            if (null == obj) {
                obj = new HashMap<String, String>(1);
            }

            try {
                rlt = JSON.parseObject(JSON.toJSONString(obj), clazz);
            } catch (Exception e) {
                logger.error("=====>>>params={}", JSON.toJSONString(obj));
                throw new ParamException("参数类型错误", e);
            }
            apiUrl = getApiUrl(request.getServletRequest());
            System.out.println("read: " + this.toString());
        } else {
            byte[] bytes = null;
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                InputStream in = inputMessage.getBody();
                byte[] buf = new byte[1024];
                for (; ; ) {
                    int len = in.read(buf);
                    if (len == -1) {
                        break;
                    }

                    if (len > 0) {
                        baos.write(buf, 0, len);
                    }
                }
                bytes = baos.toByteArray();
                rlt = JSON.parseObject(bytes, 0, bytes.length, super.getCharset().newDecoder(), clazz);
            } catch (Exception e) {
                logger.error("=====>>>params={}", JSON.parse(bytes, 0, bytes.length, super.getCharset().newDecoder()));
                throw new ParamException("参数类型错误", e);
            }
        }

        if (logger.isInfoEnabled()) {
            logger.info("=====>>>url:{};params:{}", apiUrl, XnParamParser.getLog(rlt));
        }

        return rlt;
    }

    /**
     * 获取接口路径
     *
     * @param request
     * @return
     */
    private String getApiUrl(HttpServletRequest request) {
        String targetUrl = request.getRequestURI().substring(request.getRequestURI().lastIndexOf(
                request.getServletPath()) + request.getServletPath().length());
        url.set(targetUrl);
        return url.get();
    }

    @Override
    protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        if (logger.isInfoEnabled()) {
            logger.info("=====<<< outputObject:{}", XnParamParser.getLog(obj));
        }
        OutputStream out = outputMessage.getBody();
        String text = JSON.toJSONString(obj, super.getFeatures());
        // 判断返回数据是否需要加密
        if (encryptFlag) {
            // 根据URL 过滤不需要加密的api
            if (needSecret(url.get())) {
                logger.info(" url == {}, text={}", url.get(), text);
                // 定义返回Map对象
                Map<String, String> returnData = new HashMap<String, String>();
                // 数据进行加密
                String encrypData = AESSupport.encrypt(text, AESKey);
                // 加密数据放入Map
                returnData.put(Constants.API_PARAMS_DATA_KEY, encrypData);
                // 统一转换成JSONString，后续统一处理
                text = JSON.toJSONString(returnData, super.getFeatures());
            }
        }
        logger.info("<<<返回加密后结果，outputObject:{}", text);
        byte[] bytes = text.getBytes(super.getCharset());
        out.write(bytes);
    }

    public List<String> getNoNeedSecretServices() {
        return noNeedSecretServices;
    }

    public void setNoNeedSecretServices(List<String> noNeedSecretServices) {
        System.out.println(this.toString());
        this.noNeedSecretServices = noNeedSecretServices;
    }

    /**
     * 验证请求URL是否需要加密解密处理
     *
     * @param url
     * @return
     */
    private boolean needSecret(String url) {

        if (noNeedSecretServices != null) {
            for (String temp : noNeedSecretServices) {
                if (url.contains(temp.trim())) {
                    return false;
                }
            }
        }
        return true;
    }

}
